<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        pre{
            font-size: 20px;
        }
    </style>
</head>
<body>
<pre>
1.React学习笔记    
    模块化：从代码层面上来说的，
    组件化：从UI层面是来说的，
    库：小而巧的功能，一个库迁移到另一个库容易,
    框架：大而全的功能，有一套完整的解决业务问题的方案；

    

2.虚拟DOM(virtual Document Object Model)
DOM的本质是什么：浏览器中的概念，用js对象来表示页面上的元素，并提供了操作DOM对象的API
React中的虚拟DOM: 是框架中的概念，是用js对象来模拟页面上的DOM和DOM嵌套
虚拟DOM的目的：为了实现页面中，DOM元素的高效更新



3.Diff算法
    tree diff:         
        新旧两颗DOM，逐层对比，当整颗DOM树逐层对比完毕，则所有需要被按需更新的元素，必然能够找到
    component diff      
        组件级别的对比，组件类型相同，则暂时认为此组件不需要被更新
        若果对比前后，组件类型不同，则需要移除旧组件，创建新组件，并追加到页面上
    element diff 
        在进行组件对比的时候，如果两个组件类型相同，则需要进行元素级别的对比 



4.使用webpack4.x创建项目
    运行npm init -y  快速初始项目
    在项目中创建src源代码目录和dist产品目录
    在src创建index.html
    使用cnpm安装webpack，运行cnpm i webpack webpack-cli -D
    在webpack4.x有一个很大的特性，约定默认的打包文件入口路径就是 src -> index.js 文件，打包后的输出文件是dist目录下的main.js文件
    4.x新增了mode属性，可选值为：development    production
    安装webpack-dev-server 执行命令：cnpm i webpack-dev-server -D 实现实时打包编译
    ebpack-dev-server打包好的main.js是托管到了内存中，所以在项目根目录中看不到，但是我们可以认为在项目根目录中有一个看不见的main.js
    在package.json文件针对dev-server配置
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev":"webpack-dev-server --open --port 3000 --hot --host 127.0.0.1  --progress --compress"
      },
      将页面生成到内存中去，html-webpack-plugin，自动生成打包后的html文件
      安装cnpm install html-webpack-plugin -D 
    项目上线打包优化 
      创建webpack.pub.config.js文件
      在package.json文件里配置脚本 "scripts": {"pub":"webpack --config webpack.pub.config.js"}
      每次上线打包清除dist文件cnpm i clean-webpack-plugin -D



      
5.正式在项目中使用react
      1.运行cnpm i react react-dom -S 安装包
        react: 专门用于创建组件和虚拟DOM的，同时组件的生命周期都在这个包中
        react-dom: 专门进行DOM操作的，最主要应用场景，就是ReactDOM.render()


        
6.启用JSX语法
    安装babel插件
        运行cnpm i babel-core babel-loader babel-plugin-transform-runtime -D
        如果出现类似错误信息（babel-loader版本过高）：
            babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'
            卸载原来的babel-loader,进行降级处理运行cnpm i babel-loader@7.1.5 -D即可
        运行cnpm i babel-preset-env bebel-preset-stage-0 -D
    安装能够识别转换jsx语法的包babel-preset-react
      运行cnpm i babel-preset-react -D
    在webapck.config.js文件中添加babel-loader配置项
    {test:/\.js|jsx$/,use:'babel-loader',exclude:/node_modules/}, //exclude添加排除项
    在项目根目录创建.babelrc文件
    添加.babelrc配置文件
        {
            "presets":["env","stage-0","react"],
            "plugins":["transform-runtime"]
        }  
    至此JSX语法已经基本支持了
    在jsx中写注释：推荐使用{/**/}
    为jsx中的元素添加class类名：需要使用className来替代class；htmlFor替换label的for属性
    在jsx创建DOM的时候，所有的节点，必须有唯一的根元素进行包裹
    在jsx语法中，标签必须成对出现，如果是单标签，则必须闭合



7.创建组件的几种方式
    第1种：
        使用构造函数创建组件
        在src下创建components组件目录，新建Hello.jsx文件
        如果在一个组件中return null,则表示此组件是空的，什么都不会渲染
        在组件中，必须返回一个合法的JSX虚拟DOM元素
        在webpack中配置省略文件后缀名
        resolve:{
            extensions:['.js','.jsx','.json'], //表示这几个文件后缀名，可以省略不写
            alias:{
                '@':path.join(__dirname,'./src') //@表示项目根目录中src这一层路径
            }
        }
    第2种：
        使用class关键字来创建组件
        了解es6 class关键字的作用,是实现面向对象编程的新形式
            1.class contrustor的基本使用
            2.实例属性和实例方法
            3.静态属性和静态方法
            4.使用extends关键字实现继承 
    对比：使用class关键字创建的组件，有自己的私有数据和生命周期函数；
         使用function创建的组件，只有props，没有自己的私有数据和生命周期函数 

    
    有状态组件与无状态组件之间的区别     
        用构造函数创建的组件，叫做无状态组件【无状态组件使用不多】
        用class关键字创建的组件，叫做有状态的组件  
        有状态组件和无状态组件之间的本质区别：有无state组件和有无生命周期函数
        React官方：无状态组件运行效率会比有状态组件运行效率会高一些
    组件中的props和state/data之间的区别
        props中的数据都是外界传递而来
        state/data中的数据，都是组件私有的；（通过ajax获取而来的数据，一般都是私有数据）
        props数据都是只读的
        state/data中的数据，都是可读可写的
    抽离组件
        创建评论父组件CmtList.jsx与CmtItem.jsx分别从index.js抽离出去 
        引入组件推荐使用@/components/组件名称的形式

8.组件中使用style行内样式并封装样式对象
    在jsx中如果想写行内样式，不能为style设置字符串的值
    而是应该这么写  style={{color:'red'}}
    h1 style="color:red">这是评论列表组件 h1
    在行内样式中如果是 数值类型的样式，则可以不用引号包裹，如果是字符串类型的样式值，必须要用引号包裹
    h1 style={{color:'red',fontSize:"35px",zIndex:3}}>这是评论列表组件 h1



9.引入外部样式表
    直接使用import cssObj from '@/css/cmtList.css'报错，需做如下配置
    运行cnpm i style-loader css-loader@2.2.1 -D //此处css-loader我们装的是2.2.1的版本
    然后在webpack.config.js 配置loader如下
        {test:/\.css$/,use:['style-loader','css-loader']} //打包处理css样式表的第三方loader
        此时在组件中引入的css样式表是全局作用的，所以需要配置css模块化
    css模块化配置
        {test:/\.css$/,use:['style-loader','css-loader?modules']}
        或者
        use:[
        {loader:'style-loader'},
        {
            loader:'css-loader',
            options:{
                modules:true,
                localIdentName:'[path][name]-[local]'      
            }
        
        }]
        在引入css样式表的地方使用import cssobj from '@/css/cmtItem.css'  此时的cssobj为类似如{title: "_1xAdKf4iFH0taLcX2qP92o"}的形式
    使用localIdentName自定义生成的类名格式，可选的参数有：
        [path]          表示样式表相对于根目录所在路径
        [name]          表示样式表文件名称
        [local]         表示样式的类名定义名称   
        [hash:length]   表示32位的hash值
        例子： {test:/\.css$/,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]']}
        经处理后的css样式为{title: "src-css-cmtItem-title-40eff"}
    如果不想使用css模块化
        被:global()包括的类名，不会被模块化，同理被:local()包括的类名，会被模块化
        则需要在css样式中这样写即可：:global(.test){font-style: italic;},这样就可以全局使用className="test"了
        多个类名添加样式
        写法一：后边样式会覆盖前边的样式
        h1 className={cssobj.title} className="test" 评论列表 h1
        
        写法二：字符串拼接
        h1 className={cssobj.title + ' test'} 评论列表 h1

        写法三：以数组的形式
        h1 className={[cssobj.title,'test'].join(' ')} 评论列表 h1
        
        

10.在项目中使用第三方UI库
    运行：cnpm i bootstrap@3.3.7 -S
    直接以这种形式使用 import bootcss from 'bootstrap/dist/css/bootstrap.css' 
    我们会看到如下报错：You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file
    运行：cnpm i url-loader file-loader -D
    需要我们在webpack.config.js文件配置
    {
        test:/\.ttf|woff|woff2|eot|svg$/,
        use:'url-loader'    
    }
    此时再次使用，就可以看到按钮样式已经成功加载了
    button className={[bootcss.btn,bootcss['btn-primary']].join(' ')}>按钮 button
    但是这样使用过于麻烦，故此我们自己约定处理方案
    规定： 第三方的样式表，都是以.css结尾
          自己开发的样式表，都是以.scss 或者 .less结尾 
          不为普通的.css 启用模块化，只为.scss或者.less启用模块化  
    运行 cnpm i sass-loader node-sass -D 用来解析scss文件的loader 
    在webpack.config.js 做如下配置
    {
        test:/\.scss$/,
        use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader']
    }
    此时再次使用,我们就可以正常使用第三库的样式表了，如下所示
    button className="btn btn-primary"> 按钮 button


11.React中绑定事件的注意点
    事件名称皆是由React提供的，事件名都是驼峰命名
    调用以箭头函数的形式如下
    button className="btn btn-success" onClick={()=>{this.myClickHandler('你好')}}>你点我呀 button
    在组件中以箭头函数的形式声明
    myClickHandler = (para) => {
        console.log('2222',para)
    }
    修改state中属性的值，不能直接修改this.state.属性=值 ，要使用React提供的this.setState({})即可
    在setState只会把对应的状态更新，但是不会覆盖其他的state状态
    因为setState是异步执行的所以在赋值完成后，可以通过回调函数里拿到新的状态值,如下所示
    this.setState({
        msg:'😭😿'
    },()=>{
        console.log(this.state.msg);   
    })

    在React中使用input标签规则
        如果只是把文本框的 value 属性绑定到了 state 状态，如果不提供 onchange 处理函数的话 得到的文本框是一个 readOnly 只读的文本框
        当为文本框绑定value值以后，要么提供一个readOnly，要么提供一个onchange处理函数
        默认情况下，如果页面上的表单元素，绑定了state上的状态值，那么，每当state上的状态值变化，必然会自动把最新的状态值，自动同步到页面上
        状态变化 ---> 自动更新页面
        （单向数据流）
         (React中的双向数据流需要coder手动更新到state中去)
        input type="text" className="form-control" value={this.state.msg} onChange={(e)=>this.txtChanged(e)} ref="txt" 
        在 onChange 事件中，获取文本框的值，有两种方案
            方案一：通过实践参数e来获取
            txtChanged = (e)=>{
                console.log(e.target.value)
                let val = e.target.value;
                this.setState({
                    msg:val
                })
                console.log(this.refs)
            }            
            方案二：通过ref属性获取
            txtChanged = (e)=>{
                console.log(this.refs) //{txt: input.form-control}
            }



12.React组件的生命周期
    概念，每个组件的实例，从创建、运行、直到销毁的整个过程中，会触发一系列的事件，而这些被触发的事件就叫做组件的生命周期函数；
    React组件的生命周期分为三部分
    组件创建阶段：(只执行一次)
        componentWillMount()
        render()
        componentDidMount()
    组件运行阶段：(根据 props 属性或 state 状态的改变，有选择性进行执行)
        componentWillReceiveProps(nextProps)
        shouldComponentUpdate(nextProps,nextState)
        componentWillUpdate((nextProps,nextState))
        render
        componentDidUpdate(prevProps,prevState)
    组件销毁阶段：(只执行一次)
        componentWillUnmount
        

13.扩展context特性
    在父组件共享一个context属性
    在子子孙孙组件中就不用逐层传递数据了
    案例：我们想通过改变父组件state里边的color属性，去修改子孙组件的字体颜色，就可以通过如下方式实现
    在父组件中作如下操作
        1.在父组件中定义一个function,这个函数有个固定的名为 getChildContext ,内部必须返回一个对象,这个对象用来共享给所有子对象
            getChildContext(){
                return {
                    color:this.state.color
                }
            }
        2.使用属性校验，规定传递给子组件的数据类型，需要定义一个静态的（static）固定名称为 childContextTypes 对象
            static childContextTypes = {
                color:ReactTypes.string
            } 
    在子组件中如下操作
        3.子组件要想使用父组件或者根组件共享的数据，必须要先在子组件中做数据类型校验
        static contextTypes = {
            color:ReactTypes.string
        }        

14.React路由的基本使用
        官方API：https://reacttraining.com/react-router/web/guides/philosophy
        安装cnpm i react-router-dom -S
        import {HashRouter,Route,Link} from 'react-router-dom'
        /**
        * 模块说明：
        * HashRouter: 路由的根容器，所有跟路由相关的东西都要包裹在这里边,而且一个网站中，只需要使用一个HashRouter就OK
        * Route: 路由规则，包含了两个属性，path component
        * Link： 链接类似于vue中的router-link,会被渲染成一个a标签
       */
       
        Route 创建的标签就是路由规则，path表示要匹配的路由，component表示要展示的组件
        默认中的路由规则是模糊匹配的，如果路由可以部分匹配成功，就会展示该路由对应的组件
        如果想精确匹配路由，可以添加exact属性
        如果想匹配参数，可以使用:修饰符

15.使用三方库ant
        API地址：https://ant.design/docs/react/introduce-cn
        运行cnpm i antd -S
        具体使用，参考官方文档即可
        为防止打包后项目体积过大，推荐使用按需导入，
        安装插件cnpm i babel-plugin-import -D，该插件会自动帮你导入js或css文件
        在babelrc文件配置插件项    
        "plugins":[["import", 
            {"libraryName": "antd",
            "libraryDirectory": "es",
            "style": "css"
        }]]


    

    </pre>
</body>
</html>